package com.adkun.markdown.markdown;

import org.springframework.stereotype.Component;

import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

//@Component
public class MarkdownParser implements NodeTypes {

    class Node implements NodeTypes {
        /**
         * 节点类型
         * 需要新建时指定
         */
        int type;

        /**
         * 父节点类型
         * 如果父节点类型是CODE_BLOCK，那么转HTML时将不处理
         * 如果父节点类型是ORDERED_LIST或UNORDERED_LIST，启用缩进检测
         */
        int parentType;

        String content = null;

        /**
         * 指向父节点
         */
        Node parent = null;

        /**
         * 孩子节点
         */
        LinkedList<Node> children = new LinkedList<>();

        Node() {
        }

        Node(int type) {
            this.type = type;
        }

        Node(int type, int parentType) {
            this.type = type;
            this.parentType = parentType;
        }

        Node(int type, String content, int parentType) {
            this.type = type;
            this.content = content;
            this.parentType = parentType;
        }

        Node(int type, Node parent, int parentType) {
            this.type = type;
            this.parent = parent;
            this.parentType = parentType;
        }

        Node(int type, String content, Node parent, int parentType) {
            this.type = type;
            this.parent = parent;
            this.parentType = parentType;
            this.content = content;
        }
    }

    /**
     * 根节点
     */
    private Node root = new Node(NodeTypes.ROOT);

    private boolean[] parsed;

    /**
     * 处理传入的markdown列表
     *
     * @param mdList
     */
    public String parseMd(List<String> mdList) {
        parsed = new boolean[mdList.size()];
        generateMdTree(mdList);
        List<String> htmlList = new ArrayList<>(100);
        generateHtmlList(htmlList, root, 0, 0);
        return getHtmlStr(htmlList);
    }

    private String getHtmlStr(List<String> htmlList) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < htmlList.size(); i++) {
            sb.append(htmlList.get(i));
        }
        return sb.toString();
    }

    /**
     * 将AST树生成html列表
     * 深度优先遍历
     *
     * @param htmlList
     * @param node
     * @param state    0：普通模式 1：列表模式（在每个前面加<li></li>）
     */
    private void generateHtmlList(List<String> htmlList, Node node, int state, int nthChild) {
        if (node == null) {
            return;
        }
        int nodeType = node.type;
        if (state == 1) {
            // 列表模式
            htmlList.add("<li>");
        }
        if (nodeType == ROOT && !node.children.isEmpty()) {
            // 根
            generateHtmlList(htmlList, node.children.get(0), 0, 0);
        } else if (nodeType == TITLE_ONE) {
            // 标题
            String content = "";
            if (!node.children.isEmpty()) {
                content = node.children.get(0).content;
                content = parseToHtmlInline(content);
            }
            htmlList.add("<h1>" + content + "</h1>");
        } else if (nodeType == TITLE_TWO) {
            String content = "";
            if (!node.children.isEmpty()) {
                content = node.children.get(0).content;
                content = parseToHtmlInline(content);
            }
            htmlList.add("<h2>" + content + "</h2>");
        } else if (nodeType == TITLE_THREE) {
            String content = "";
            if (!node.children.isEmpty()) {
                content = node.children.get(0).content;
                content = parseToHtmlInline(content);
            }
            htmlList.add("<h3>" + content + "</h3>");
        } else if (nodeType == TITLE_FOUR) {
            String content = "";
            if (!node.children.isEmpty()) {
                content = node.children.get(0).content;
                content = parseToHtmlInline(content);
            }
            htmlList.add("<h4>" + content + "</h4>");
        } else if (nodeType == TITLE_FIVE) {
            String content = "";
            if (!node.children.isEmpty()) {
                content = node.children.get(0).content;
                content = parseToHtmlInline(content);
            }
            htmlList.add("<h5>" + content + "</h5>");
        } else if (nodeType == TITLE_SIX) {
            String content = "";
            if (!node.children.isEmpty()) {
                content = node.children.get(0).content;
                content = parseToHtmlInline(content);
            }
            htmlList.add("<h6>" + content + "</h6>");
        } else if (nodeType == CODE_BLOCK) {
            // 代码块
            htmlList.add("<pre><code>");
            if (!node.children.isEmpty()) {
                generateHtmlList(htmlList, node.children.get(0), 0, 0);
            }
            htmlList.add("</code></pre>");
        } else if (nodeType == QUOTE_BLOCK) {
            // 引用块
            htmlList.add("<blockquote>");
            if (!node.children.isEmpty()) {
                generateHtmlList(htmlList, node.children.get(0), 0, 0);
            }
            htmlList.add("</blockquote>");
        } else if (nodeType == ORDERED_LIST) {
            // 有序列表
            htmlList.add("<ol>");
            if (!node.children.isEmpty()) {
                generateHtmlList(htmlList, node.children.get(0), 1, 0);
            }
            htmlList.add("</ol>");
        } else if (nodeType == UNORDERED_LIST) {
            // 无序列表
            htmlList.add("<ul>");
            if (!node.children.isEmpty()) {
                generateHtmlList(htmlList, node.children.get(0), 1, 0);
            }
            htmlList.add("</ul>");
        } else if (nodeType == NORMAL_LINE) {
            // 代码块里的行
            htmlList.add(node.content + "\n");
        } else if (nodeType == WAIT_FOR_PARSE) {
            // 等待处理的行（行内语法）
            String parsedInline = parseToHtmlInline(node.content);
            htmlList.add("<p>" + parsedInline + "</p>");
        } else if (nodeType == SPACE_LINE) {
            htmlList.add("<br />");
        } else if (nodeType == HORIZON) {
            htmlList.add("<hr />");
        }

        if (state == 1) {
            // 列表模式
            htmlList.add("</li>");
        }

        // 下一个子节点
        if (nodeType != ROOT) {
            List<Node> siblings = node.parent.children;
            int childrenNum = siblings.size();
            if (nthChild + 1 < childrenNum) {
                generateHtmlList(htmlList, siblings.get(nthChild + 1), state, nthChild + 1);
            }
        }
    }

    /**
     * 处理一行
     *
     * @param line
     * @return
     */
    private String parseToHtmlInline(String preLine) {
        String line = preLine;
        for (int i = 0; i < line.length(); i++) {
            // 图片
            if (i < line.length() - 4 && line.charAt(i) == '!' && line.charAt(i + 1) == '[') {
                int index1 = line.indexOf(']', i + 1);
                if (index1 != -1 && line.charAt(index1 + 1) == '(' && line.indexOf(')', index1 + 2) != -1) {
                    int index2 = line.indexOf(')', index1 + 2);
                    String picName = line.substring(i + 2, index1);
                    String picPath = line.substring(index1 + 2, index2);
                    line = line.replace(line.substring(i, index2 + 1), "<img alt='" + picName + "' src='" + picPath + "' />");
                }
            }
            // 链接
            if (i < line.length() - 3 && ((i > 0 && line.charAt(i) == '[' && line.charAt(i - 1) != '!') || (line.charAt(0) == '['))) {
                int index1 = line.indexOf(']', i + 1);
                if (index1 + 1 < line.length() && index1 != -1 && line.charAt(index1 + 1) == '(' && line.indexOf(')', index1 + 2) != -1) {
                    int index2 = line.indexOf(')', index1 + 2);
                    String linkName = line.substring(i + 1, index1);
                    String linkPath = line.substring(index1 + 2, index2);
                    line = line.replace(line.substring(i, index2 + 1), "<a href='" + linkPath + "' target='_blank'> " + linkName + "</a>");
                }
            }
            // 行内引用
            if (i < line.length() - 1 && line.charAt(i) == '`' && line.charAt(i + 1) != '`') {
                int index = line.indexOf('`', i + 1);
                if (index != -1 && index >= i) {
                    String quoteName = line.substring(i + 1, index);
                    line = line.replace(line.substring(i, index + 1), "<code>" + quoteName + "</code>");
                }
            }
            // 粗体
            if (i < line.length() - 2 && line.charAt(i) == '*' && line.charAt(i + 1) == '*') {
                if (i - 6 > -1 && "<code>".equals(line.substring(i - 6, i))) {
                    // 说明有行内引用
                    continue;
                }
                line = line.replaceFirst("\\*\\*", "<strong>");
                i = i + 8;
                int index = line.indexOf("**", i);
                if (index != -1 && index >= i) {
                    String quoteName = line.substring(i, index);
                    quoteName = stripAsterisk(quoteName);
                    line = line.replaceFirst(quoteName + "\\*\\*", quoteName + "</strong>");
                }
            }
            // 删除线
            if (i < line.length() - 2 && line.charAt(i) == '~' && line.charAt(i + 1) == '~') {
                if (i - 6 > -1 && "<code>".equals(line.substring(i - 6, i))) {
                    // 说明有行内引用
                    continue;
                }
                line = line.replaceFirst("~~", "<del>");
                int index = line.indexOf("~~");
                if (index != -1 && index >= i) {
                    String quoteName = line.substring(i, index);
                    line = line.replaceFirst(quoteName + "~~", quoteName + "</del>");
                }
            }
            // 斜体
            if (i < line.length() - 2 && line.charAt(i) == '*' && line.charAt(i + 1) != '*') {
                if (i - 6 > -1 && "<code>".equals(line.substring(i - 6, i))) {
                    // 说明有行内引用
                    continue;
                } else if (i - 7 > -1 && "<code>".equals(line.substring(i - 7, i))) {
                    // 说明有行内引用
                    continue;
                }
                int index = line.indexOf('*', i);
                if (index != -1 && index + 1 < line.length() && line.charAt(index + 1) != '*') {
                    index = line.indexOf('*', index + 1);
                    if (index < i) {
                        return preLine;
                    }
                    String quoteName = line.substring(i + 1, index);
                    line = line.replaceFirst("\\*" + quoteName + "\\*", "<i>" + quoteName + "</i>");
                }
            }
        }
        return line;
    }

    /**
     * 去除*号
     * @param str
     * @return
     */
    private String stripAsterisk(String str) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < str.length(); i++) {
            char ch = str.charAt(i);
            if (ch != '*') {
                sb.append(ch);
            }
        }
        return sb.toString();
    }

    /**
     * 生成AST语法树
     *
     * @param mdList
     */
    private void generateMdTree(List<String> mdList) {
        generateBlock(mdList, root, 1);
    }

    /**
     * 判断该行嵌套层数
     *
     * @param row
     * @return
     */
    private int getLevel(String row) {
        if (row == null || row.length() == 0) {
            return 0;
        }
        int level = 0;
        while (row.length() > 0) {
            int type = getType(row);
            if (type == TAB) {
                row = row.substring(4);
                level++;
            } else if (type == QUOTE_BLOCK || type == UNORDERED_LIST) {
                row = row.substring(2);
                level++;
            } else if (type == ORDERED_LIST) {
                if (Character.isDigit(row.charAt(0)) && Character.isDigit(row.charAt(1))) {
                    row = row.substring(4);
                } else if (Character.isDigit(row.charAt(0)) && row.charAt(1) == '.'
                        && row.charAt(2) == ' ') {
                    row = row.substring(3);
                }
                level++;
            } else {
                row = "";
            }

        }
        return level;
    }

    /**
     * 生成可嵌套的块
     *
     * @param mdList
     * @param blockNode
     * @param k
     * @return
     */
    private int generateBlock(List<String> mdList, Node blockNode, int k) {
        int i = k;
        for (; i < mdList.size() - 1; i++) {
            String rowStr = mdList.get(i);
            // 如果要实现缩进则不能用trim()
            int type = getType(rowStr);
            int level = getLevel(rowStr);

            if (parsed[i]) {
                continue;
            }

            if (type == CODE_BLOCK) {
                // 代码块
                // 生成代码块，代码块内部保留原样式
                Node newCodeBlockNode = new Node(CODE_BLOCK, blockNode, blockNode.type);
                blockNode.children.add(newCodeBlockNode);
                setMdListRow(mdList, type, i);
                parsed[i] = true;
                i = generateCodeBlock(mdList, newCodeBlockNode, i);
            } else if (type == HORIZON) {
                // 水平分隔线
                Node newHorizonNode = new Node(HORIZON, root, ROOT);
                root.children.add(newHorizonNode);
                parsed[i] = true;
            } else if (type == QUOTE_BLOCK || type == ORDERED_LIST || type == UNORDERED_LIST
                    || (type == TAB && level > 0)) {
                // 引用块、有序列表、无序列表
                // 可嵌套块，可生成
                if (type == TAB) {
                    // 四个空格为一个缩进，判断缩进是否有效
                    // 缩进有效：当根节点下方有引用块、有序列表、无序列表时；并且level>0
                    // 缩进无效：当作待处理行
                    if (!blockNode.children.isEmpty()) {
                        Node next = blockNode.children.get(blockNode.children.size() - 1);
                        if (next.type == ORDERED_LIST || next.type == UNORDERED_LIST
                                || next.type == QUOTE_BLOCK) {
                            // 如果有可以嵌套的，就进入下一个
                            setMdListRow(mdList, TAB, i);
                            generateBlock(mdList, next, i);
                            break;
                        } else {
                            // 没有可以嵌套的，当作WAIT_FOR_PARSE
                            // 待处理
                            Node newWaitNode = new Node(WAIT_FOR_PARSE, mdList.get(i), root, ROOT);
                            root.children.add(newWaitNode);
                            parsed[i] = true;
                        }
                    } else {
                        // 没有找到嵌套，当作WAIT_FOR_PARSE
                        Node newWaitNode = new Node(WAIT_FOR_PARSE, mdList.get(i), root, ROOT);
                        root.children.add(newWaitNode);
                        parsed[i] = true;
                    }
                } else {
                    Node cur = root;
                    while (level >= 0) {
                        level--;
                        rowStr = mdList.get(i);
                        type = getType(rowStr);
                        setMdListRow(mdList, type, i);
                        if (!cur.children.isEmpty()) {
                            // 查看本层最后一个是否匹配本行的type
                            cur = cur.children.get(cur.children.size() - 1);
                            if (cur.type == type) {
                                // 匹配，则将该行首去除，递归
                                rowStr = mdList.get(i);
                                type = getType(rowStr);
                                setMdListRow(mdList, type, i);

                                if (type == WAIT_FOR_PARSE || type == TAB) {
                                    // 如果该行嵌套结束，则递归处理下一行
                                    Node newBlockNode = new Node(WAIT_FOR_PARSE, rowStr, cur, cur.type);
                                    cur.children.add(newBlockNode);
                                    parsed[i] = true;
                                    i = generateBlock(mdList, root, i + 1);
                                    break;
                                } else {
                                    // 该行未嵌套结束，新建一层
                                    Node newBlockNode = new Node(type, cur, cur.type);
                                    cur.children.add(newBlockNode);
                                    cur = newBlockNode;
                                }
                            } else {
                                // 匹配不上，从root开始
                                if (type == WAIT_FOR_PARSE) {
                                    Node newBlockNode = new Node(type, rowStr, root, ROOT);
                                    root.children.add(newBlockNode);
                                    parsed[i] = true;
                                    i = generateBlock(mdList, root, i + 1);
                                    break;
                                } else {
                                    Node newBLockNode = new Node(type, root, ROOT);
                                    root.children.add(newBLockNode);
                                    cur = newBLockNode;
                                }
                            }
                        } else if (level == -1) {
                            // 只剩WAIT_FOR_PARSE
                            Node newBlockNode = new Node(type, rowStr, cur, cur.type);
                            cur.children.add(newBlockNode);
                            parsed[i] = true;
                            i = generateBlock(mdList, root, i + 1);
                            break;
                        } else {
                            // 当前节点没有孩子
                            if (type == WAIT_FOR_PARSE || type == TAB) {
                                Node newBlockNode = new Node(WAIT_FOR_PARSE, rowStr, cur, cur.type);
                                cur.children.add(newBlockNode);
                                parsed[i] = true;
                                i = generateBlock(mdList, root, i + 1);
                                break;
                            } else {
                                Node newBLockNode = new Node(type, cur, cur.type);
                                cur.children.add(newBLockNode);
                                cur = newBLockNode;
                            }
                        }
                    }
                }

            } else if (type == TITLE_ONE || type == TITLE_TWO
                    || type == TITLE_THREE || type == TITLE_FOUR
                    || type == TITLE_FIVE || type == TITLE_SIX) {
                // 标题块
                // 无子节点
                setMdListRow(mdList, type, i);
                Node newTitleNode = new Node(type, blockNode, blockNode.type);
                blockNode.children.add(newTitleNode);
                Node titleContent = new Node(WAIT_FOR_PARSE, mdList.get(i), newTitleNode, newTitleNode.type);
                newTitleNode.children.add(titleContent);
                parsed[i] = true;
                generateBlock(mdList, root, i + 1);
                break;
            } else if (type == SPACE_LINE) {
                // 空行
                Node newSpaceNode = new Node(type, "", blockNode, blockNode.type);
                blockNode.children.add(newSpaceNode);
                parsed[i] = true;
                generateBlock(mdList, root, i + 1);
                break;
            } else {
                // 待处理
                Node newWaitNode = new Node(WAIT_FOR_PARSE, mdList.get(i), blockNode, blockNode.type);
                blockNode.children.add(newWaitNode);
                parsed[i] = true;
                generateBlock(mdList, root, i + 1);
                break;
            }
        }
        return i;
    }

    /**
     * 去除行首MD特征
     *
     * @param mdList
     * @param type
     * @param k
     */
    private void setMdListRow(List<String> mdList, int type, int k) {
        if (type == TITLE_ONE) {
            mdList.set(k, mdList.get(k).substring(2));
        } else if (type == TITLE_TWO) {
            mdList.set(k, mdList.get(k).substring(3));
        } else if (type == TITLE_THREE) {
            mdList.set(k, mdList.get(k).substring(4));
        } else if (type == TITLE_FOUR) {
            mdList.set(k, mdList.get(k).substring(5));
        } else if (type == TITLE_FIVE) {
            mdList.set(k, mdList.get(k).substring(6));
        } else if (type == TITLE_SIX) {
            mdList.set(k, mdList.get(k).substring(7));
        } else if (type == ORDERED_LIST) {
            mdList.set(k, mdList.get(k).substring(3));
        } else if (type == UNORDERED_LIST) {
            mdList.set(k, mdList.get(k).substring(2));
        } else if (type == QUOTE_BLOCK) {
            mdList.set(k, mdList.get(k).substring(2));
        } else if (type == CODE_BLOCK) {
            mdList.set(k, " ");
        } else if (type == TAB) {
            mdList.set(k, mdList.get(k).substring(4));
        }
    }

    /**
     * 生成代码块（不可嵌套）
     *
     * @param mdList        markdown原文列表
     * @param codeBlockNode 以blockNode为根创建
     * @param k             之前遍历到的地方
     * @return 新遍历到的地方
     */
    private int generateCodeBlock(List<String> mdList, Node codeBlockNode, int k) {
        int i = k + 1;
        for (; i < mdList.size(); i++) {
            String rowStr = mdList.get(i);
            if (!rowStr.contains("```")) {
                // 代码块未结束，将本行原本内容放入节点
                Node codeLineNode = new Node(NORMAL_LINE, rowStr, codeBlockNode, codeBlockNode.type);
                codeBlockNode.children.add(codeLineNode);
                parsed[i] = true;
            } else {
                // 代码块结束
                return i;
            }
        }
        return i;
    }

    /**
     * 获取行首的类型
     *
     * @param str
     * @return
     */
    private int getType(String str) {
        int len = str.length();
        if (len >= 4) {
            String prefix = str.substring(0, 4);
            char preCh = prefix.charAt(0);
            char preCh2 = prefix.charAt(1);
            char preCh3 = prefix.charAt(2);
            char preCh4 = prefix.charAt(3);
            if (Character.isDigit(preCh) && Character.isDigit(preCh2)
                    && preCh3 == '.' && preCh4 == ' ') {
                return ORDERED_LIST;
            }
            if ("    ".equals(prefix)) {
                // 缩进
                return TAB;
            }
        }
        if (len >= 3) {
            String prefix = str.substring(0, 3);
            char preCh = prefix.charAt(0);
            char preCh2 = prefix.charAt(1);
            char preCh3 = prefix.charAt(2);
            if ("```".equals(prefix)) {
                // 代码块
                return CODE_BLOCK;
            }
            if (Character.isDigit(preCh) && preCh2 == '.' && preCh3 == ' ') {
                // 有序列表
                return ORDERED_LIST;
            }
            if ("---".equals(prefix)) {
                // 水平分隔线
                return HORIZON;
            }
        }
        if (len >= 2) {
            String prefix = str.substring(0, 2);
            if ("> ".equals(prefix)) {
                // 引用块
                return QUOTE_BLOCK;
            }

            char preCh = prefix.charAt(0);
            char preCh2 = prefix.charAt(1);
            if (preCh == '*' || preCh == '+' || preCh == '-') {
                if (preCh2 == ' ') {
                    // 无序列表
                    return UNORDERED_LIST;
                }
            }

            if (prefix.charAt(0) == '#') {
                // 标题
                int count = 0;
                boolean ok = false;
                for (int i = 0; i < str.length(); i++) {
                    char ch = str.charAt(i);
                    if (ch == '#') {
                        count++;
                    } else if (ch == ' ') {
                        ok = true;
                    }
                }
                if (ok) {
                    switch (count) {
                        case 1:
                            return TITLE_ONE;
                        case 2:
                            return TITLE_TWO;
                        case 3:
                            return TITLE_THREE;
                        case 4:
                            return TITLE_FOUR;
                        case 5:
                            return TITLE_FIVE;
                        case 6:
                            return TITLE_SIX;
                        default:
                            return WAIT_FOR_PARSE;
                    }
                }
            }
        }
        // 空行
        if (len == 0) {
            return SPACE_LINE;
        } else if (len == 1) {
            if (str.charAt(0) == ' ' || str.charAt(0) == '\n') {
                return SPACE_LINE;
            }
        }
        return WAIT_FOR_PARSE;
    }
}